﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;

using Radar.Utils;
using Radar.Optimization;
using Jage.Utils;

namespace Radar.Navigation
{
    internal class LinkDetector
        : SingletonBase<LinkDetector>
    {

        internal void DetectAndAssignLinksAndWaypoints(NavMesh navMesh)
        {
            navMesh.Waypoints = new WaypointUniqueCollection();

            int columnCount = navMesh.Grid.Cells.GetLength(0);
            int rowCount = navMesh.Grid.Cells.GetLength(1);

            for (int x = 0; x < columnCount; x++)
            {
                for (int y = 0; y < rowCount; y++)
                {
                    DetectAndAssignLinksAndWaypoints(navMesh, navMesh.Grid.Cells[x, y]);
                }
            }
        }

        private void DetectAndAssignLinksAndWaypoints(NavMesh navMesh, GridCell cell)
        {
            int zoneCount = cell.ZonesStarting.Length;
            for (int i = 0; i < zoneCount; i++)
            {
                DetectAndAssignLinksAndWaypoints(Direction.Right, navMesh, cell.ZonesStarting[i]);
                DetectAndAssignLinksAndWaypoints(Direction.Down, navMesh, cell.ZonesStarting[i]);
            }
        }

        private void DetectAndAssignLinksAndWaypoints(Direction direction, NavMesh navMesh, NavZone zone)
        {
            GridCellRange cells = GetCellsInDirection(direction, navMesh, zone);
            NavZone[] zones = cells.GetZonesTouching();
            int zoneCount = zones.Length;
            for (int i = 0; i < zoneCount; i++)
            {
                if (zone != zones[i])
                {
                    DetectAndAssignLinkAndWaypoints(navMesh, direction, zone, zones[i]);
                }
            }
        }

        private void DetectAndAssignLinkAndWaypoints(NavMesh navMesh, Direction direction, NavZone zone, NavZone otherZone)
        {
            switch (direction)
            {
                case Direction.Right:
                    if (zone.Right == otherZone.Left)
                    {
                        Line? intersection = Line.GetIntersection(new Line(zone.Top, zone.Bottom), new Line(otherZone.Top, otherZone.Bottom));
                        if (intersection.HasValue && intersection.Value.Length > 0)
                        {
                            var link = new Link(navMesh.Waypoints.GetWaypointFor(new Point(zone.Right, intersection.Value.Begin)),
                                                navMesh.Waypoints.GetWaypointFor(new Point(zone.Right, intersection.Value.End)), 
                                                zone, otherZone, 
                                                intersection.Value.Length);
                            zone.AddLinkAndWaypoints(link);
                            otherZone.AddLinkAndWaypoints(link);
                        }
                    }
                    break;
                case Direction.Down:
                    if (zone.Bottom == otherZone.Top)
                    {
                        Line? intersection = Line.GetIntersection(new Line(zone.Left, zone.Right), new Line(otherZone.Left, otherZone.Right));
                        if (intersection.HasValue && intersection.Value.Length > 0)
                        {
                            var link = new Link(navMesh.Waypoints.GetWaypointFor(new Point(intersection.Value.Begin, zone.Bottom)), 
                                                navMesh.Waypoints.GetWaypointFor(new Point(intersection.Value.End, zone.Bottom)), 
                                                zone, otherZone, 
                                                intersection.Value.Length);
                            zone.AddLinkAndWaypoints(link);
                            otherZone.AddLinkAndWaypoints(link);
                        }
                    }
                    break;
            }
        }

        private GridCellRange GetCellsInDirection(Direction direction, NavMesh navMesh, NavZone zone)
        {
            switch (direction)
            {
                case Direction.Right:
                    return navMesh.Grid.GetCellsInRectangle(zone.Right, zone.Top, zone.Right, zone.Bottom);
                case Direction.Down:
                    return navMesh.Grid.GetCellsInRectangle(zone.Left, zone.Bottom, zone.Right, zone.Bottom);
                default:
                    throw new ArgumentException(String.Format("Direction \"{0}\" is not supported.", direction.ToString()));
            }
        }

        private enum Direction
        {
            Right,
            Down
        }

    }
}
